File size: 10,828 Bytes
0a5dc9a
 
 
 
 
 
 
 
 
 
 
 
08ac5e5
f191235
08ac5e5
 
 
0a5dc9a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
08ac5e5
 
0a5dc9a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e81512b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a5dc9a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
08ac5e5
0a5dc9a
 
 
 
 
 
 
 
08ac5e5
 
 
 
0a5dc9a
 
08ac5e5
0a5dc9a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
## 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("""
<details style="margin-bottom: 18px;">
  <summary style="font-size: 18px; cursor: pointer; font-weight: bold;">
    How to Choose the Correct Load Face
  </summary>
  <div style="padding-left: 12px; margin-top: 10px;">

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

  <h3><b>Load Type: bend_bottom</b></h3>
  <p>Use this when the bracket or part is <b>loaded from below</b>, such as:</p>
  <ul>
    <li>A shelf bracket being pressed upward</li>
    <li>A support arm being bent from underneath</li>
  </ul>

  <p><b>Choose the face that is physically on the bottom of the part</b>, i.e.:</p>
  <ul>
    <li>The largest downward-facing surface</li>
    <li>The surface that would contact a support or wall in real usage</li>
  </ul>

  <p>πŸ’‘ <i>Tip:</i> In most STEP files this is a face with a downward normal  
  (<code>normal β‰ˆ [0, 0, -1]</code>).</p>

  <hr>

  <h3><b>Load Type: tension</b></h3>
  <p>Use this when the bracket or part is pulled <b>backwards</b> or <b>outwards</b>, such as:</p>
  <ul>
    <li>A wall bracket being pulled away from a mounting plane</li>
    <li>A hook experiencing outward tension</li>
  </ul>

  <p><b>Choose the face that is physically the rear/back face of the part</b>, i.e.:</p>
  <ul>
    <li>The flat face that would mount to a wall</li>
    <li>A large face with outward-pointing orientation</li>
  </ul>

  <p>πŸ’‘ <i>Tip:</i> Often the normal points along Β±X  
  (<code>normal β‰ˆ [Β±1, 0, 0]</code>).</p>

  <hr>

  <h3><b>If You Are Unsure</b></h3>
  <ul>
    <li>Choose the face that best represents the real-life load direction.</li>
    <li>Picking an incorrect face has <b>minimal effect</b> on the mesh output.</li>
  </ul>

  </div>
</details>
""")

    gr.HTML("""
<details style="margin-bottom: 18px;">
  <summary style="font-size: 18px; cursor: pointer; font-weight: bold;">
    3D Visualization Mode Guidance
  </summary>
  <div style="padding-left: 12px; margin-top: 10px;">

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

  <ul>
    <li><b>wireframe</b>: 3D interactive wireframe of the final optimized mesh</li>
    <li><b>highlight</b>: wireframe with refined regions shown in red</li>
    <li><b>heatmap</b>: continuous heatmap quantifying refinement magnitude</li>
  </ul>

  <p><b>Heatmap color scale:</b></p>
  <ul>
    <li><b>Red</b>: very small predicted mesh size β†’ highest refinement</li>
    <li><b>Blue</b>: very large predicted mesh size β†’ coarsest regions</li>
  </ul>

  <p>The heatmap provides a smooth, global visualization of refinement intensity across the mesh.</p>

  </div>
</details>
""")

    
    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)